package com.xing.lei.happy.logic;

import javax.sql.DataSource;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 表数据比较工具类
 * 
 * 用于比较两个不同数据库中相同结构表的数据差异，并生成对应的SQL脚本
 */
public class TableDataComparator {

    private final DataSource sourceDataSource;
    private final DataSource targetDataSource;

    /**
     * 构造函数
     * 
     * @param sourceDataSource 源数据源
     * @param targetDataSource 目标数据源
     */
    public TableDataComparator(DataSource sourceDataSource, DataSource targetDataSource) {
        this.sourceDataSource = sourceDataSource;
        this.targetDataSource = targetDataSource;
    }

    /**
     * 比较两个表的数据并生成SQL脚本
     * 
     * @param tableName  表名
     * @return 包含差异数据操作的SQL脚本
     * @throws SQLException SQL异常
     */
    public String compareTableData(String tableName) throws SQLException {
        // 自动获取主键列
        List<String> keyColumns = getPrimaryKeyColumns(sourceDataSource, tableName);
        
        if (keyColumns.isEmpty()) {
            throw new SQLException("表 " + tableName + " 没有主键，无法进行数据比较");
        }
        
        return compareTableData(tableName, keyColumns);
    }

    /**
     * 比较两个表的数据并生成SQL脚本
     * 
     * @param tableName  表名
     * @param keyColumns 主键列名列表
     * @return 包含差异数据操作的SQL脚本
     * @throws SQLException SQL异常
     */
    public String compareTableData(String tableName, List<String> keyColumns) throws SQLException {
        StringBuilder script = new StringBuilder();
        script.append("-- 表 ").append(tableName).append(" 数据差异比较脚本\n");
        script.append("-- 生成时间: ").append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date())).append("\n\n");

        // 获取源表和目标表的数据
        List<Map<String, Object>> sourceData = getTableData(sourceDataSource, tableName);
        List<Map<String, Object>> targetData = getTableData(targetDataSource, tableName);

        // 转换为目标表数据的映射，以主键为键
        Map<List<Object>, Map<String, Object>> targetDataMap = new HashMap<>();
        for (Map<String, Object> row : targetData) {
            List<Object> keyValues = getKeyValues(row, keyColumns);
            targetDataMap.put(keyValues, row);
        }

        // 转换为源表数据的映射，以主键为键
        Map<List<Object>, Map<String, Object>> sourceDataMap = new HashMap<>();
        for (Map<String, Object> row : sourceData) {
            List<Object> keyValues = getKeyValues(row, keyColumns);
            sourceDataMap.put(keyValues, row);
        }

        // 查找新增的记录（在源表中存在但在目标表中不存在）
        for (Map.Entry<List<Object>, Map<String, Object>> entry : sourceDataMap.entrySet()) {
            List<Object> keyValues = entry.getKey();
            Map<String, Object> sourceRow = entry.getValue();

            if (!targetDataMap.containsKey(keyValues)) {
                // 在目标表中不存在，生成INSERT语句
                script.append(generateInsertSQL(tableName, sourceRow)).append(";\n");
            } else {
                // 在目标表中存在，比较其他字段
                Map<String, Object> targetRow = targetDataMap.get(keyValues);
                if (!isRowEqual(sourceRow, targetRow, keyColumns)) {
                    // 数据不一致，生成UPDATE语句
                    script.append(generateUpdateSQL(tableName, sourceRow, targetRow, keyColumns)).append(";\n");
                }
            }
        }

        // 查找删除的记录（在目标表中存在但在源表中不存在）
        for (Map.Entry<List<Object>, Map<String, Object>> entry : targetDataMap.entrySet()) {
            List<Object> keyValues = entry.getKey();

            if (!sourceDataMap.containsKey(keyValues)) {
                // 在源表中不存在，生成DELETE语句
                Map<String, Object> targetRow = entry.getValue();
                script.append(generateDeleteSQL(tableName, targetRow, keyColumns)).append(";\n");
            }
        }

        return script.toString();
    }

    /**
     * 获取表的所有数据
     * 
     * @param dataSource 数据源
     * @param tableName  表名
     * @return 表数据列表
     * @throws SQLException SQL异常
     */
    private List<Map<String, Object>> getTableData(DataSource dataSource, String tableName) throws SQLException {
        List<Map<String, Object>> dataList = new ArrayList<>();
        String sql = "SELECT * FROM " + tableName;

        try (Connection connection = dataSource.getConnection();
             PreparedStatement statement = connection.prepareStatement(sql);
             ResultSet resultSet = statement.executeQuery()) {

            ResultSetMetaData metaData = resultSet.getMetaData();
            int columnCount = metaData.getColumnCount();

            while (resultSet.next()) {
                Map<String, Object> row = new HashMap<>();
                for (int i = 1; i <= columnCount; i++) {
                    String columnName = metaData.getColumnName(i);
                    Object value = resultSet.getObject(i);
                    row.put(columnName, value);
                }
                dataList.add(row);
            }
        }

        return dataList;
    }

    /**
     * 获取行记录的主键值列表
     * 
     * @param row        行记录
     * @param keyColumns 主键列名列表
     * @return 主键值列表
     */
    private List<Object> getKeyValues(Map<String, Object> row, List<String> keyColumns) {
        List<Object> keyValues = new ArrayList<>();
        for (String keyColumn : keyColumns) {
            keyValues.add(row.get(keyColumn));
        }
        return keyValues;
    }

    /**
     * 判断两个行记录除主键外的其他字段是否相等
     * 
     * @param sourceRow  源行记录
     * @param targetRow  目标行记录
     * @param keyColumns 主键列名列表
     * @return 是否相等
     */
    private boolean isRowEqual(Map<String, Object> sourceRow, Map<String, Object> targetRow, List<String> keyColumns) {
        for (Map.Entry<String, Object> entry : sourceRow.entrySet()) {
            String columnName = entry.getKey();
            // 跳过主键列的比较
            if (keyColumns.contains(columnName)) {
                continue;
            }

            Object sourceValue = entry.getValue();
            Object targetValue = targetRow.get(columnName);

            if (sourceValue == null && targetValue == null) {
                continue;
            }

            if (sourceValue == null || targetValue == null) {
                return false;
            }

            if (!sourceValue.equals(targetValue)) {
                return false;
            }
        }

        return true;
    }

    /**
     * 生成INSERT SQL语句
     * 
     * @param tableName 表名
     * @param row       行记录
     * @return INSERT SQL语句
     */
    private String generateInsertSQL(String tableName, Map<String, Object> row) {
        StringBuilder sql = new StringBuilder();
        sql.append("INSERT INTO ").append(tableName).append(" (");

        // 添加列名
        boolean first = true;
        for (String columnName : row.keySet()) {
            if (!first) {
                sql.append(", ");
            }
            sql.append(columnName);
            first = false;
        }

        sql.append(") VALUES (");

        // 添加值
        first = true;
        for (Object value : row.values()) {
            if (!first) {
                sql.append(", ");
            }
            sql.append(formatValue(value));
            first = false;
        }

        sql.append(")");
        return sql.toString();
    }

    /**
     * 生成UPDATE SQL语句
     * 
     * @param tableName  表名
     * @param sourceRow  源行记录
     * @param targetRow  目标行记录
     * @param keyColumns 主键列名列表
     * @return UPDATE SQL语句
     */
    private String generateUpdateSQL(String tableName, Map<String, Object> sourceRow, Map<String, Object> targetRow, List<String> keyColumns) {
        StringBuilder sql = new StringBuilder();
        sql.append("UPDATE ").append(tableName).append(" SET ");

        // 更新非主键字段
        boolean first = true;
        for (Map.Entry<String, Object> entry : sourceRow.entrySet()) {
            String columnName = entry.getKey();
            // 跳过主键列的更新
            if (keyColumns.contains(columnName)) {
                continue;
            }

            if (!first) {
                sql.append(", ");
            }
            sql.append(columnName).append(" = ").append(formatValue(entry.getValue()));
            first = false;
        }

        // 添加WHERE条件
        sql.append(" WHERE ");
        first = true;
        for (String keyColumn : keyColumns) {
            if (!first) {
                sql.append(" AND ");
            }
            sql.append(keyColumn).append(" = ").append(formatValue(sourceRow.get(keyColumn)));
            first = false;
        }

        return sql.toString();
    }

    /**
     * 生成DELETE SQL语句
     * 
     * @param tableName  表名
     * @param row        行记录
     * @param keyColumns 主键列名列表
     * @return DELETE SQL语句
     */
    private String generateDeleteSQL(String tableName, Map<String, Object> row, List<String> keyColumns) {
        StringBuilder sql = new StringBuilder();
        sql.append("DELETE FROM ").append(tableName).append(" WHERE ");

        boolean first = true;
        for (String keyColumn : keyColumns) {
            if (!first) {
                sql.append(" AND ");
            }
            sql.append(keyColumn).append(" = ").append(formatValue(row.get(keyColumn)));
            first = false;
        }

        return sql.toString();
    }

    /**
     * 格式化值用于SQL语句
     * 
     * @param value 值
     * @return 格式化后的值
     */
    private String formatValue(Object value) {
        if (value == null) {
            return "NULL";
        }

        if (value instanceof Number) {
            return value.toString();
        }

        if (value instanceof java.util.Date) {
            // 对于日期类型，使用通用格式
            return "TIMESTAMP '" + new Timestamp(((java.util.Date) value).getTime()) + "'";
        }

        // 对字符串中的单引号进行转义
        String strValue = value.toString().replace("'", "''");
        return "'" + strValue + "'";
    }

    /**
     * 获取表的主键列名列表
     * 
     * @param dataSource 数据源
     * @param tableName  表名
     * @return 主键列名列表
     * @throws SQLException SQL异常
     */
    public List<String> getPrimaryKeyColumns(DataSource dataSource, String tableName) throws SQLException {
        List<String> keyColumns = new ArrayList<>();
        String schema = null;
        String table = tableName;

        // 解析schema和表名（如果表名包含schema）
        if (tableName.contains(".")) {
            String[] parts = tableName.split("\\.");
            schema = parts[0];
            table = parts[1];
        }

        // 查询主键列
        try (Connection connection = dataSource.getConnection()) {
            DatabaseMetaData metaData = connection.getMetaData();
            try (ResultSet resultSet = metaData.getPrimaryKeys(null, schema, table)) {
                while (resultSet.next()) {
                    String columnName = resultSet.getString("COLUMN_NAME");
                    keyColumns.add(columnName);
                }
            }
        }

        return keyColumns;
    }
}